package gee

import (
	"html/template"
	"net/http"
	"path"
	"strings"
)

// HandlerFunc 定义请求处理方法类型
type HandlerFunc func(*Context)

// Engine 引擎
type Engine struct {
	*RouterGroup                     // 继承RouterGroup
	router        *router            // 路由表
	groups        []*RouterGroup     // 存储所有路由分组
	htmlTemplates *template.Template // html模板
	funcMap       template.FuncMap   // 所有的自定义模板渲染函数
}

// New 引擎实例
func New() *Engine {
	engine := &Engine{
		router: newRouter(),
	}
	// 设置路由分组
	engine.RouterGroup = &RouterGroup{
		engine: engine,
	}
	// 设置所有路由分组
	engine.groups = []*RouterGroup{engine.RouterGroup}
	return engine
}

// addRoute 添加路由
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
	engine.router.addRouter(method, pattern, handler)
}

// GET 添加get方法的路由
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
	engine.addRoute("GET", pattern, handler)
}

// POST 添加post方法的路由
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
	engine.addRoute("POST", pattern, handler)
}

// Run 运行引擎，监听端口
func (engine *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, engine)
}

// ServeHTTP 请求调度器
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var middlewares []HandlerFunc
	// 便利所有路由分组
	for _, group := range engine.groups {
		// 如果请求路径和路由组的前缀匹配，则将路由组的中间件都添加至上下文中
		if strings.HasPrefix(req.URL.Path, group.prefix) {
			middlewares = append(middlewares, group.middlewares...)
		}
	}
	c := newContext(w, req)
	c.handlers = middlewares
	c.engine = engine
	engine.router.handle(c)
}

// SetFuncMap 设置模板方法映射
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
	engine.funcMap = funcMap
}

// LoadHTTPGlob 加载模板，这是模板所在的路径
func (engine *Engine) LoadHTTPGlob(pattern string) {
	engine.htmlTemplates = template.Must(template.New("").Funcs(engine.funcMap).ParseGlob(pattern))
}

// RouterGroup 路由组
type RouterGroup struct {
	prefix      string        // 前缀
	middlewares []HandlerFunc // 中间件
	parent      *RouterGroup  // 上一级组（父组）
	engine      *Engine       // 引擎
}

// Group 创建新的路由分组，记录所有分组在engine.groups
func (group *RouterGroup) Group(prefix string) *RouterGroup {
	engine := group.engine
	// 定义新的路由分组
	newGroup := &RouterGroup{
		prefix: group.prefix + prefix, // 父级前缀加上当前前缀作为当前路由的前缀
		parent: group,                 // 父级分组
		engine: engine,                // 引擎和父级一样，保持不变
	}
	// 记录当前分组到引擎下
	engine.groups = append(engine.groups, newGroup)
	return newGroup
}

// addRouter 添加路由
func (group *RouterGroup) addRouter(method string, comp string, handler HandlerFunc) {
	pattern := group.prefix + comp // 请求的完整路径
	// 调用了group.engine.router.addRoute来实现了路由的映射
	group.engine.router.addRouter(method, pattern, handler)
}

// GET 添加GET方法的路由
func (group *RouterGroup) GET(pattern string, handler HandlerFunc) {
	group.addRouter("GET", pattern, handler)
}

// POST 添加POST方法的路由
func (group *RouterGroup) POST(pattern string, handler HandlerFunc) {
	group.addRouter("POST", pattern, handler)
}

// Use 给路由组添加中间件
func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
	group.middlewares = append(group.middlewares, middlewares...)
}

// createStaticHandler 创建静态文件处理函数
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
	absolutePath := path.Join(group.prefix, relativePath)             // 路径拼接
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) // 将路径的处理交给fs
	return func(c *Context) {
		file := c.Param("filepath")
		// 验证权限
		if _, err := fs.Open(file); err != nil {
			c.Status(http.StatusNotFound)
			return
		}

		// 系统的文件HTTP请求处理
		fileServer.ServeHTTP(c.Writer, c.Req)
	}
}

// Static 用户可以将磁盘上的某个文件夹root映射到路由relativePath
func (group *RouterGroup) Static(relativePath string, root string) {
	// 将root的文件系统作为relativePath的FileServer
	handler := group.createStaticHandler(relativePath, http.Dir(root))
	// 采用*通配符匹配静态资源
	urlPattern := path.Join(relativePath, "/*filepath")
	// 注册GET路由
	group.GET(urlPattern, handler)
}
